package com.as.measurements.client.core;

/**
 * - 02/2008: Class created by Nicolas Richasse
 * 
 * Changelog:
 * 	- class created
 * 	- iperf line parsing fixed and improved
 * 
 * To do:
 * 	- ...
 *
 * Old notes:
 *	- The ParseLine results variable still ends up with a blank 0th string which may or may not ever matter (DC)
 */

import java.io.*;

import java.sql.Timestamp;
import java.util.Vector;
import java.util.regex.*;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.as.measurements.client.core.models.JperfStreamResult;
import com.as.measurements.client.core.models.Measurement;
import com.as.measurements.client.listener.MeasurementsListener;

public class QoSAgent {
	private String command;
	private Process process;
	// private JPerfUI frame;
	private JperfStreamResult _finalResults;

	private BufferedReader input;
	private BufferedReader errors;

	private boolean isServerMode;
	private MeasurementsListener _listener = null;
	private String interf;
	private final String TIEMPO_INICIAL = "0";
	private final String TIEMPO_FINAL = "5";
	private 		java.sql.Timestamp stamp = null;
	/**
	 * Logger
	 */
	private Log _logger = LogFactory.getLog(getClass().getName());

	public QoSAgent(boolean isServerMode, String command,
			MeasurementsListener listener, String interf) {
		_logger.debug("constructor ...." + interf);
		this.isServerMode = isServerMode;
		this.command = command;
		this.interf = interf;

		_listener = listener;
		
		// set the date
		java.util.Date utilDate = new java.util.Date(); 
		long lnMilisegundos = utilDate.getTime();
		stamp = new java.sql.Timestamp(lnMilisegundos);
	}

	public void run() {
		_logger.debug(" run ..." + interf);
		try {

			process = Runtime.getRuntime().exec(command);
			int exitVal = process.waitFor();
			_logger.debug("Process exitValue: " + exitVal);

			_logger.debug(" System call done! - " + interf);
			// read in the output from Iperf
			input = new BufferedReader(new InputStreamReader(process
					.getInputStream()));
			errors = new BufferedReader(new InputStreamReader(process
					.getErrorStream()));

			String input_line = null;
			while ((input_line = input.readLine()) != null) {
				parseLine(input_line);

				// frame.logMessage(input_line);
			}

			if (_listener != null) {
				
				if (_finalResults == null){
					_finalResults = new JperfStreamResult(-1, interf);

					_finalResults.setStamp(stamp);
				}
				
				_listener.notifyMeasurement(_finalResults);
			
				
			}
			

			String error_line = null;
			while ((error_line = errors.readLine()) != null) {
				_logger.error(" errors reading iperf output: " + error_line
						+ "\n");
			}

			_logger.debug(" System call done! - " + interf);
		} catch (Exception e) {
			// don't do anything?
			_logger.error(" Exception execuiting system iperf call ", e);
		} finally {
			quit();
		}
	}

	public synchronized void quit() {
		if (process != null) {

			try {
				Thread.sleep(100);
			} catch (InterruptedException ie) {
			}

			if (!isServerMode) {
				if (input != null) {
					try {
						input.close();
					} catch (Exception e) {
						// nothing
					} finally {
						input = null;
					}
				}

				if (errors != null) {
					try {
						errors.close();
					} catch (Exception e) {
						// nothing
					} finally {
						errors = null;
					}
				}

				try {
					process.getInputStream().close();
				} catch (Exception e) {
					// nothing
				}

				try {
					process.getOutputStream().close();
				} catch (Exception e) {
					// nothing
				}

				try {
					process.getErrorStream().close();
				} catch (Exception e) {
					// nothing
				}
			}

			process.destroy();

			try {
				process.waitFor();
			} catch (Exception ie) {
				// nothing
			}

			process = null;

		}
	}

	public void parseLine(String line) {
		
		_logger.debug(" parsing system command result");
		// only want the actual output lines
		if (line.matches("\\[[ \\d]+\\]\\s*[\\d]+.*")) {
			Pattern p = Pattern.compile("[-\\[\\]\\s]+");
			// ok now break up the line into id#, interval, amount transfered,
			// format
			// transferred, bandwidth, and format of bandwidth
			String[] results = p.split(line);
			_logger.debug(line);
			// // get the ID # for the stream
			// Integer temp = new Integer(results[1].trim());
			// int id = temp.intValue();
			//
			// boolean found = false;
			// JperfStreamResult streamResult = new JperfStreamResult(id,
			// interf);
			// for (int i = 0; i < finalResults.size(); ++i)
			// {
			// if ((finalResults.elementAt(i)).getID() == id)
			// {
			// streamResult = finalResults.elementAt(i);
			// found = true;
			// break;
			// }
			// }
			//
			// if (!found)
			// {
			// finalResults.add(streamResult);
			// }
			// // this is TCP or Client UDP
			// if (results.length == 9)
			// {
			// Double start = new Double(results[2].trim());
			// Double end = new Double(results[3].trim());
			// Double bw = new Double(results[7].trim());
			//				
			// Measurement M = new Measurement(start.doubleValue(),
			// end.doubleValue(), bw.doubleValue(), results[8]);
			// streamResult.addBW(M);
			// // frame.addNewStreamBandwidthMeasurement(id, M);
			// }
			// else
			/*
			 * [ 4] local 192.168.3.64 port 34952 connected with 192.168.3.16
			 * port 5001 [ ID] Interval Transfer Bandwidth 
			 * [ 4] 0.0- 1.0 sec 11548 KBytes 94597 Kbits/sec 
			 * [ 4] 1.0- 2.0 sec 11512 KBytes 94303 Kbits/sec 
			 * [ 4] 2.0- 3.0 sec 11582 KBytes 94880 Kbits/sec 
			 * [ 4] 3.0- 4.0 sec 11695 KBytes 95809 Kbits/sec 
			 * [ 4] 4.0- 5.0 sec 11639 KBytes 95350 Kbits/sec 
			 * [ 4] 0.0- 5.0 sec 57977 KBytes 94987 Kbits/sec 
			 * [ 4] Sent 40387 datagrams 
			 * [ 3] local 192.168.3.64 port 5001 connected with 192.168.3.16 port 45928 
			 * [ 3] 0.0- 1.0 sec 11651 KBytes 95444 Kbits/sec 0.027 ms 6/ 8122 (0.074%) 
			 * [ 3] 1.0- 2.0 sec 11703 KBytes 95868 Kbits/sec 0.036 ms 0/ 8152 (0%) 
			 * [ 4] WARNING: did not receive ack of last datagram after 10 tries. 
			 * [ 3] 2.0- 3.0 sec 11645 KBytes 95397 Kbits/sec 0.062 ms 27/ 8139 (0.33%) 
			 * [ 3] 3.0- 4.0 sec 11682 KBytes 95703 Kbits/sec 0.023 ms 0/ 8138 (0%) 
			 * [ 3] 4.0- 5.0 sec 11668 KBytes 95585 Kbits/sec 0.030 ms 10/ 8138 (0.12%) 
			 * [ 3] 0.0- 5.0 sec 58414 KBytes 95579 Kbits/sec 0.164 ms 43/40734 (0.11%)
			 * 
			 * last line is the server report and is in what we are interested
			 */
			if ((results.length == 13) || (results.length == 14)) {
				_logger.debug(" ++++++++++++++++++++++++++++++++++++++++++++++++++++  sum up line parsed" );
				String start = results[2].trim();
				String end = results[3].trim();
				double startD = Math.floor(Double.parseDouble(start));
				double endD = Math.floor(Double.parseDouble(end));

				if (start.startsWith(TIEMPO_INICIAL) && (end.startsWith(TIEMPO_FINAL))) {

					Integer temp = new Integer(results[1].trim());
					int id = temp.intValue();

					_finalResults = new JperfStreamResult(id, interf);
					
					// set the date

					_finalResults.setStamp(stamp);

					Double bw = new Double(results[7].trim());

					Measurement B = new Measurement(startD, endD, bw.doubleValue(), results[8]);
					_finalResults.addBW(B);

					Double jitter = new Double(results[9].trim());
					Measurement J = new Measurement(startD, endD, jitter.doubleValue(), results[10]);
					_finalResults.addJitter(J);

					Double packet_loss = new Double(0);
					if (results.length == 13)
						packet_loss = new Double(results[12].trim().substring(1,results[12].trim().length()-2));
					else if (results.length == 14)
						packet_loss = new Double(results[13].trim().substring(1,results[13].trim().length()-2));
					Measurement PL = new Measurement(startD, endD, packet_loss.doubleValue(), "%");
					_finalResults.addPacketLoss(PL);
				}
			}
		}
	}
}
